#include <string.h>
#include <stdlib.h>

#include "tick.h"
#include "timer.h"
#include "hardware.h"
#include "SensorChannelADC.h"
#include "SensorChannels.h"

static void ConfigDMA4SensorChannelADC(void *DMACacheBase, int DMACacheSizeInByte);
static void SensorChannelADCLowlevelInit(void);


#define SENSOR_CHANNEL_DMA_SIZEWORD		64
#define SEBSIR_CHANNEL_DMA_SHIFFT		6
static uint16_t SensorChannelDMACache[SENSOR_CHANNEL_DMA_SIZEWORD * 6];
static int ADCConvertDone = FALSE;

int OpenSensorChannelsADC() {
	ConfigDMA4SensorChannelADC(SensorChannelDMACache, sizeof(SensorChannelDMACache));
	SensorChannelADCLowlevelInit();
	return 0;
}

void CloseSensorChannelsADC() {
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, DISABLE);
	ADC_Cmd(ADC1, DISABLE);
	ADC_DMACmd(ADC1, DISABLE);
	DMA_Cmd(DMA1_Channel1, DISABLE);
}

int ReadSensorChannelsADC(int channel, uint16_t *A, uint16_t *B) {
	uint32_t Buffer[2];
	int i;
	
	ADCConvertDone = FALSE;
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, ENABLE);
	// ConfigDMA4SensorChannelADC(SensorChannelDMACache, sizeof(SensorChannelDMACache));
	// ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	while (!ADCConvertDone);
	ADCConvertDone = FALSE;
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, ENABLE);
	while (!ADCConvertDone);
	
	i = channel * 2;
	Buffer[0] = 0;
	Buffer[1] = 0;
	for(; i < (SENSOR_CHANNEL_DMA_SIZEWORD * 6); i += 6) {
		Buffer[0] += SensorChannelDMACache[i];
		Buffer[1] += SensorChannelDMACache[i + 1];
	}
	*A = Buffer[0] >> SEBSIR_CHANNEL_DMA_SHIFFT;
	*B = Buffer[1] >> SEBSIR_CHANNEL_DMA_SHIFFT;
	return 1;
}

void SensorChannelADCDMADoneCallback() {
	ADCConvertDone = TRUE;
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, DISABLE);
}

static void ConfigDMA4SensorChannelADC(void *DMACacheBase, int DMACacheSizeInByte) {
	DMA_InitTypeDef DMA_InitStructure;
	const uint32_t ADC1DRAddress = 0x4001244C;
	
	// ADC1 is in DMA1_Channel1
	DMA_DeInit(DMA1_Channel1);
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1DRAddress;
	/* buffer base address */
	DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DMACacheBase;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	/* buffer length in half-word */
	DMA_InitStructure.DMA_BufferSize = DMACacheSizeInByte / sizeof(uint16_t);
	
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	// DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);  
	
	/* Enable DMA1 channel1 */
	DMA_ITConfig(ADC_DMA_Channel, DMA_IT_TC, ENABLE);
	DMA_Cmd(DMA1_Channel1, ENABLE);
}

static void SensorChannelADCLowlevelInit() {
	ADC_InitTypeDef ADC_InitStructure;
	
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	// ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	// ADC_InitStructure.ADC_NbrOfChannel = 3;
	ADC_InitStructure.ADC_NbrOfChannel = 6;
	ADC_Init(ADC1, &ADC_InitStructure);
	
	/* ADC1 regular channels configuration */ 
	ADC_RegularChannelConfig(ADC1, CH1_IOA_ADC_CH, 1, ADC_SampleTime_28Cycles5);
	ADC_RegularChannelConfig(ADC1, CH1_IOB_ADC_CH, 2, ADC_SampleTime_28Cycles5); 
	
	ADC_RegularChannelConfig(ADC1, CH2_IOA_ADC_CH, 3, ADC_SampleTime_28Cycles5);
	ADC_RegularChannelConfig(ADC1, CH2_IOB_ADC_CH, 4, ADC_SampleTime_28Cycles5); 
	
	ADC_RegularChannelConfig(ADC1, CH3_IOA_ADC_CH, 5, ADC_SampleTime_28Cycles5);
	ADC_RegularChannelConfig(ADC1, CH3_IOB_ADC_CH, 6, ADC_SampleTime_28Cycles5); 

	// ADC_RegularChannelConfig(ADC1, BATTERY_MONITOR_ADC_CH, 7, ADC_SampleTime_28Cycles5);
	
	/* Enable ADC1 DMA */
	ADC_DMACmd(ADC1, ENABLE);
	
	/* Enable ADC1 */
	ADC_Cmd(ADC1, ENABLE);

	/* Enable ADC1 reset calibration register */   
	ADC_ResetCalibration(ADC1);
	/* Check the end of ADC1 reset calibration register */
	while(ADC_GetResetCalibrationStatus(ADC1));

	/* Start ADC1 calibration */
	ADC_StartCalibration(ADC1);
	/* Check the end of ADC1 calibration */
	while(ADC_GetCalibrationStatus(ADC1));
	
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
}

